#include "stdafx.h"
#pragma hdrstop

#include "ThreadUtils.h"
#include "OSMutex.h"

#include <MMSystem.h>
#include <Process.h>
#include <cmath>

#pragma comment(lib, "winmm.lib")

using namespace std;
using namespace ThreadUtils;

static unsigned int sMainThreadId = ::GetCurrentThreadId();
static bool sInitialized = false;
static OSMutex sMutex;

void ThreadUtils::PrecisionSleepFor(double ms)
{
	if(ms < 0)
		return;

	// MSDN warns against frequent callss to timeBeginPeriod(), so we use a static RAAI object
	// to set timing precision once, and clear it on application exit.
	// we also do not want to call timeBeginPeriod() unless PrecisionSleepFor() is actually
	// used, so we use RAAI object that is local to the function.
	static class SetHighestPrecision
	{
	public:
		SetHighestPrecision()
			: mPrecision(0)
		{
			OSMutex::Lock lock(sMutex);
			if(!sInitialized)
			{
				TIMECAPS tc = {0,0};
				::timeGetDevCaps(&tc, sizeof(tc));
				mPrecision = tc.wPeriodMin;
				::timeBeginPeriod(mPrecision);
				sInitialized = true;
			}
		}

		~SetHighestPrecision()
		{
			::timeEndPeriod(mPrecision);
		}

	private:
		UINT mPrecision;
		bool mbIntialized;
		OSMutex mMutex;
	}setHighestPrecision;

	// Use "dithering" to achieve sub-milliseconds accuracy of mean sleep time
	int sleepTime = static_cast<int>(::floor(ms)) + static_cast<int>(::rand() < (RAND_MAX+1) * ::fmod(ms, 1));
	::Sleep(sleepTime);
}

void ThreadUtils::SleepFor(int ms)
{
	::Sleep(ms);
}

bool ThreadUtils::InMainThread()
{
	return ::GetCurrentThreadId() == sMainThreadId;
}

int ThreadUtils::NumberOfProcessors()
{
	SYSTEM_INFO sysinfo;
	::GetSystemInfo(&sysinfo);
	return sysinfo.dwNumberOfProcessors;
}

void ThreadUtils::PrecisionSleepUntil(PrecisionTime wakeup)
{
	PrecisionSleepFor(PrecisionTime::SignedDiff(wakeup, PrecisionTime::Now()));
}